package de.hs.rm;

import java.awt.AWTException;
import java.awt.Color;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;

import javax.bluetooth.*;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;

public class BHMainDesktop {
	
    public BHGUIDesktop gui = null;
    private volatile ListenThread lThread = null;
    private Robot robot = null;
    private PointerInfo pMouseInfo = null;
	private Point pMouse = null;
    
    public BHMainDesktop()
    {
    	try {
			robot = new Robot();
		} catch (AWTException e) {
			e.printStackTrace();
		}
    	
    	gui = new BHGUIDesktop();
    	gui.btStartServer.addActionListener(new BStartServerActionListener());
    	gui.btStopServer.addActionListener(new BStopServerActionListener());
    	gui.lbStatus.setBackground(Color.RED);
    }

    public static void main(String[] args) throws IOException, InterruptedException {
    	new BHMainDesktop();
    	
    }
    
    private class BStartServerActionListener implements ActionListener
    {
//    	private JButton button;
//		
//    	public BStartServerActionListener(JButton button) {
//			this.button = button;
//		}
		@Override
		public void actionPerformed(ActionEvent arg0) {
			
			try {
				if(lThread == null)
				{
					gui.lbStatus.setBackground(Color.YELLOW);
					lThread = new ListenThread();
					lThread.start();
				}
		    } catch (Exception  e) {System.out.println("Exception Occured: " + e.toString());}
			
		}    	
    }
    
    private class BStopServerActionListener implements ActionListener
    {
//    	private JButton button;
//		
//    	public BStopServerActionListener(JButton button) {
//			this.button = button;
//		}
		@Override
		public void actionPerformed(ActionEvent arg0) {
			
			try {
				if(lThread != null)
				{
					lThread.interrupt();
					stopServer();
				}
		    } catch (Exception  e) {System.out.println("Exception Occured: " + e.toString());}
			
		}    	
    }
    
    private void restartServer()
    {
    		lThread = null;
	    	gui.lbStatus.setBackground(Color.YELLOW);
			lThread = new ListenThread();
			lThread.start();
    }
    
    private void stopServer()
    {
    		lThread = null;
    		gui.lbStatus.setBackground(Color.RED);
			gui.updateTaLog("Server Stopped!");
    }
    
    private class ListenThread extends Thread
    {

    	public final UUID uuid = new UUID(                              //the uid of the service, it has to be unique,
    			"0000110100001000800000805F9B34FB", false); //it can be generated randomly
        public final String name = "BlueHid";                       //the name of the service
        public final String url  =  "btspp://localhost:" + uuid         //the service url
                                    + ";name=" + name 
                                    + ";authenticate=false;encrypt=false;";
        LocalDevice local = null;
        StreamConnectionNotifier server = null;
        StreamConnection conn = null;
    	DataInputStream din = null;
    	DataOutputStream don = null;
    	private boolean connected = false;
    	
    	@Override
    	public void interrupt()
    	{
    		super.interrupt();
    		if (server != null && conn == null)
    		{
				try {
					server.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
    		}
    	}
    	
    	@Override
		public void run() {
			try {
	            gui.updateTaLog("Setting device to be discoverable...");
	            local = LocalDevice.getLocalDevice();
	            //local.setDiscoverable(DiscoveryAgent.GIAC);
	            gui.updateTaLog("Start advertising service..." +  local.getBluetoothAddress());
	            server = (StreamConnectionNotifier)Connector.open(url);
	            gui.updateTaLog("Waiting for incoming connection...");
	            conn = server.acceptAndOpen();
	            	
	            gui.updateTaLog("Client Connected...");
	            connected = true;
	            gui.lbStatus.setBackground(Color.GREEN);
	            din = new DataInputStream(conn.openInputStream());
	            don = new DataOutputStream(conn.openOutputStream());
	            
	            byte[] buffer = new byte[1024];
	            int bytes;
	            
	            while (true) {
	            	try {
	                    // Read from the InputStream
	                    bytes = din.read(buffer);                    
	                    // construct a string from the valid bytes in the buffer
	                    String msg = new String(buffer, 0, bytes);
	                    gui.updateTaLog(msg);
	                    if (msg.contains("ping:"))
	                    	returnPing();
	                    if(msg.contains("mouse:"))
	                    	evaluateMouse(msg);
	                    if(msg.contains("keyboard:"))
	                    	evaluateKeyboard(msg);
	                    if(msg.contains("gamePad:"))
	                    	evaluateGamePad(msg);
	                    if(msg.contains("remote:"))
	                    	evaluateRemote(msg);
	                } catch (IOException e) {
	                	stopThread();
	                }                
	                catch(StringIndexOutOfBoundsException e)
	                {
	                	throw new InterruptedException();	                	
	                }

	            }
			} catch(InterruptedException ex) {
				retartThread();
	        } catch(InterruptedIOException ex) {
	        	stopThread();			
	        } catch (BluetoothStateException e) {
	        	retartThread();
			} catch (IOException e) {
				retartThread();
			}
		}
    	
    	public void writeMsg(String msg)
	    {
	    	byte[] msgBuffer = msg.getBytes();
	        try {
	            don.write(msgBuffer);
	        } catch (IOException e) {}
	        
	        if (don != null) {
	            try {
	            	don.flush();
	            } catch (IOException e) {}
	        }
	    }
    	
    	private void returnPing()
    	{
    		writeMsg("ping:");
    		//restartServer();
    	}
    	
    	private void stopThread()
    	{
    		if (connected)
    			writeMsg("disconnect:");
    		try {
    			if(din != null)
    				din.close();
    			if(conn != null)
    				conn.close();
    			if(server != null)
    				server.close();    			
    			//stopServer();
    		}
    		catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}	
    	}
    	
    	private void retartThread()
    	{
    		if (connected)
    			writeMsg("disconnect:");
    		try {
    			if(din != null)
    				din.close();
    			if(conn != null)
    				conn.close();
    			if(server != null)
    				server.close();    			
    			restartServer();
    		}
    		catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}	
    	}		
    }

// Mouse ###################################################################
    private void evaluateMouse(String msg)
    {
    	if(msg.contains("move"))
    		moveMouse(msg);
    	else if(msg.contains("leftClick"))
    		clickMouse(InputEvent.BUTTON1_MASK);
    	else if(msg.contains("leftDown"))
    		downMouse(InputEvent.BUTTON1_MASK);
    	else if(msg.contains("leftUp"))
    		upMouse(InputEvent.BUTTON1_MASK);
    	else if(msg.contains("rightClick"))
    		clickMouse(InputEvent.BUTTON3_MASK);
    	else if(msg.contains("rightDown"))
    		downMouse(InputEvent.BUTTON3_MASK);
    	else if(msg.contains("rightUp"))
    		upMouse(InputEvent.BUTTON3_MASK);
    	else if(msg.contains("scroll"))
    		scrollMouse(msg);
    }

    private void clickMouse(int buttonMask)
    {
    	robot.mousePress(buttonMask);
    	robot.mouseRelease(buttonMask);
    }
    
    private void upMouse(int buttonMask)
    {
    	robot.mouseRelease(buttonMask);
    }
    
    private void downMouse(int buttonMask)
    {
    	robot.mousePress(buttonMask);//InputEvent.BUTTON1_MASK);
    }
    
    private void moveMouse(String msg)
    {
    	int x = getXFromString(msg);
    	int y = getYFromString(msg);
    	pMouseInfo = MouseInfo.getPointerInfo();
		pMouse = pMouseInfo.getLocation();
		int px = (int)pMouse.getX();
		int py = (int)pMouse.getY();
		robot.mouseMove(px + x, py + y);
		//gui.updateTaLog("x: " + x +" y: " + y);
    }
    
    private void scrollMouse(String msg)
    {
    	int y = getYFromString(msg);    	
		robot.mouseWheel(y);
		//gui.updateTaLog("x: " + x +" y: " + y);
    }
    
    private int getXFromString(String mouseMsg) {
		int start = mouseMsg.indexOf("xA") + 2;
		int end = mouseMsg.indexOf("xE");
		String xString = mouseMsg.substring(start, end);
    	return Integer.parseInt(xString);
	}

	private int getYFromString(String mouseMsg)
    {
		int start = mouseMsg.indexOf("yA") + 2;
		int end = mouseMsg.indexOf("yE");
		String yString = mouseMsg.substring(start, end);
    	return Integer.parseInt(yString);
    }
	
// Keyboard ###################################################################

	private boolean altGr = false;
	private boolean shift = false;
	private boolean alt = false;
	private boolean ctrl = false;
	
	private void evaluateKeyboard(String msg)
	{
		if(msg.contains("keydown"))
		{
			int keyCode = getXFromString(msg);
			
			switch (keyCode)
			{
				case 18://KeyEvent.VK_ALT
					toggleModKey(alt, keyCode);
					alt = !alt;
					break;
				case 65406://KeyEvent.VK_ALT_GRAPH
					toggleModKey(altGr, KeyEvent.VK_ALT_GRAPH);
					altGr = !altGr;
					break;
				case 17: //KeyEvent.VK_CONTOL
					toggleModKey(ctrl, keyCode);
					ctrl = !ctrl;
					break;
				case 16: //KeyEvent.VK_SHIFT
					toggleModKey(shift, keyCode);
					shift = !shift;
					break;
				default:
					robot.keyPress(keyCode);
					robot.keyRelease(keyCode);
					break;				
			}
		}
		
//		if(msg.contains("keydown"))
//		{
//			robot.keyPress(keyCode);
//			robot.keyRelease(keyCode);
//		}
//		else if (msg.contains("keyup"))
//		{
//			robot.keyRelease(keyCode);
//		}
	}
	
	private void toggleModKey(boolean key, int keyCode)
	{
		if(!key)
			robot.keyPress(keyCode);
		else
			robot.keyRelease(keyCode);
	}

// GamePad ###################################################################
	private void evaluateGamePad(String msg)
	{
		if(msg.contains("press"))
		{
			if(msg.contains("left"))
			{
				robot.keyPress(KeyEvent.VK_LEFT);
				//robot.keyRelease(KeyEvent.VK_LEFT);
			}
			else if(msg.contains("right"))
			{
				robot.keyPress(KeyEvent.VK_RIGHT);
				//robot.keyRelease(KeyEvent.VK_RIGHT);
			}
			else if(msg.contains("up"))
			{
				robot.keyPress(KeyEvent.VK_UP);
				//robot.keyRelease(KeyEvent.VK_UP);
			}
			else if(msg.contains("down"))
			{
				robot.keyPress(KeyEvent.VK_DOWN);
				//robot.keyRelease(KeyEvent.VK_DOWN);
			}
		}
		else if(msg.contains("release"))
		{
			if(msg.contains("left"))
				robot.keyRelease(KeyEvent.VK_LEFT);
			else if(msg.contains("right"))
				robot.keyRelease(KeyEvent.VK_RIGHT);
			else if(msg.contains("up"))
				robot.keyRelease(KeyEvent.VK_UP);
			else if(msg.contains("down"))
				robot.keyRelease(KeyEvent.VK_DOWN);
		}	
	}
// GamePad ###################################################################
	private void evaluateRemote(String msg)
	{
		if(msg.contains(":script:"))
		{
			String cmd = msg.substring("remote:script:".length());
			try {
			    // Execute a command without arguments
			    String command = "ls";
			    Process child = Runtime.getRuntime().exec(cmd);
			} catch (IOException e) {
			}
		}
	
	}

}
